package com.ebupt.migu.music.commons.sms;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.ebupt.migu.music.commons.constants.StrategyConstants;
import com.ebupt.migu.music.commons.utils.HttpUtils;
import com.ebupt.migu.propagate.MegamarketConf;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class Sender {

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${megamarket.ip}")
    private String MEGAMARKET_IP;

    @Value("${megamarket.sendMarketingSMS}")
    private String MEGAMARKET_SENDMARKETINGSMS;

    @Value("${megamarket.saltValue}")
    private String MEGAMARKET_SALTVALUE;

//    @Value("${megamarket.sendChannel}")
//    private String MEGAMARKET_SENDCHANNEL;

    @Value("${megamarket.accessCode}")
    private String MEGAMARKET_ACCESSCODE;

    /**
     * 登陆短信模板ID
     */
    public static String LOGIN_CODE = "20100001";

    /**
     * 短信接收成功
     */
    private static String MSG_RETURN_CODE_SUCCESS = "000000";
    private static String MSG_RETURN_CODE_FAIL = "000001";
    // 源号码(默认为10658830, 手机终端收到消息后显示的号码), 对于有扩展子号码的需求开放
    private static String SOURCE_ADDR = "10658830";

    private static String MEGAMARKET_RETURN_CODE_SUCCESS = "0";

    /**
     * MD5加密
     *
     * @param str 加密前的字符串
     * @return 返回加密后的字符串
     */
    private static String getMD5String(String str) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5".toUpperCase());
            byte[] digest = md.digest(str.getBytes());
            return bytesToString(digest);
        } catch (Exception e) {
            log.error("md5加密失败，错误原因：[{}]", e.getMessage());
        }
        return null;
    }

    /**
     * 转换成String
     *
     * @param data byte数组
     * @return 转换后的string
     */
    private static String bytesToString(byte[] data) {
        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        char[] temp = new char[data.length * 2];
        for (int i = 0; i < data.length; i++) {
            byte b = data[i];
            temp[(i * 2)] = hexDigits[(b >>> 4 & 0xF)];
            temp[(i * 2 + 1)] = hexDigits[(b & 0xF)];
        }
        return new String(temp).toUpperCase();
    }

    /**
     * 生成header 请求头（用于合法性验证）
     *
     * @return RequestHeader
     */
    private static RequestHeader getRequestHeader() {
        Date now = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String date = sdf.format(now);
        String msgId = "2010100" + date + "00000000";
        RequestHeader rh = new RequestHeader();
        rh.setMsgId(msgId);
        rh.setDeviceId("2010");
        rh.setDevicePwd(getMD5String(msgId + "123abc!@#"));
        rh.setAccessPlatformID("100");
        /**
         * 2023-06-18 业务说明：当前下发短信不需要再校验手机号是否属于移动手机号 参数说明【说明来自《短信模板消息下发接口规范》】： 1：需要状态报告(系统处理失败结果及手机终端回条)，0（及其他）：不需要, 默认为0
         * 因当前所下发通道无法获得上行信息，此参数留作备用，先全当0处理，功能待条件具备后实现；
         */
        rh.setNeedReport("1");
        return rh;
    }

    /**
     * 通用短信接口
     *
     * @param msgCode 短信模板ID,由消息下发平台统一配置分配
     * @param tel 目的手机号码,数组，可填多个
     * @param tplVars 模板中变量值(如模板中没有变量，可以为空)
     */
    public static boolean sendSMS(String msgCode, String[] tel, String[] tplVars) {
        Response response;
        log.info("短信模板[{}]，手机号码：[{}]，短信内容：[{}]", msgCode, tel, tplVars);
        try {
            response = new MessageSenderServiceLocator().getMessageSender().sendSmTplMessage(getRequestHeader(),
                SOURCE_ADDR, tel, msgCode, tplVars);
            log.info("response info:{}", response.toString());
            log.info("发送短信结果返回：code[{}], message[{}]", response.getReturnCode(), response.getDescription());
            if (response.getReturnCode().equals(MSG_RETURN_CODE_SUCCESS)) {
                return true;
            }
        } catch (Exception e) {
            log.error("短信发送失败，error[{}]", e.getMessage());
        }
        return false;
    }

    /**
     * 短信下发
     *
     * @param sid
     * @param msgCode
     * @param tel
     * @param tplVars
     * @return
     */
    @Async("send")
    public void sendSMS(String sid, String msgCode, String[] tel, String[] tplVars, Map<String, Object> strategy) {
        Response response;
        log.info("短信模板[{}]，手机号码：[{}]，短信内容：[{}]", msgCode, tel, tplVars);
        String today = DateUtil.format(new Date(),"yyyyMMdd");
        // 短信发送进度
        String key_incr = sid + "_" + today + "_incr";
        // 成功数
        String key_success = sid + "_" + today + "_success";
        // 失败数
        String key_fail = sid + "_" + today + "_fail";
        try {
            response = new MessageSenderServiceLocator().getMessageSender().sendSmTplMessage(getRequestHeader(),
                SOURCE_ADDR, tel, msgCode, tplVars);
            log.info("发送短信结果返回：手机号码:[{}], code[{}], message[{}]", tel, response.getReturnCode(),
                response.getDescription());
            redis_incr(key_incr);
            if (response.getReturnCode().equals(MSG_RETURN_CODE_SUCCESS)) {
                redis_incr(key_success);
            } else {
                redis_incr(key_fail);
            }
        } catch (Exception e) {
            log.error("短信发送失败，手机号码:[{}], error[{}]", tel, e.getMessage());
        }
    }

    /**
     * 原子自增
     *
     * @return
     */
    private void redis_incr(String key_name) {
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        RedisAtomicLong redisAtomicLong = new RedisAtomicLong(key_name, redisTemplate.getConnectionFactory());
        redisAtomicLong.getAndIncrement();
    }

    /**
     * 测试用例
     *
     * @return
     */
    public static Map<String, String> sendMsg() {
        Map<String, String> map = new HashMap<>();
        Response response;
        String[] tel = {"13688361924"};
        String[] tplVars = {"9527"};
        try {
            response = new MessageSenderServiceLocator().getMessageSender().sendSmTplMessage(getRequestHeader(),
                SOURCE_ADDR, tel, LOGIN_CODE, tplVars);
            map.put("code", response.getReturnCode());
            map.put("desc", response.getDescription());
            log.info("map info:{}", map.toString());
            if (!MSG_RETURN_CODE_SUCCESS.equals(response.getReturnCode())) {
                log.error("短信发送失败，错误代码：[{}],错误原因：[{}]", response.getReturnCode(), response.getDescription());
            }
        } catch (Exception e) {
            log.error("短信发送失败，error[{}]", e.getMessage());
        }
        return map;
    }

    /**
     * 短信下发
     *
     * @param sid 策略id
     * @param tels 电话号码，多个,中间用,隔开
     * @param tplVars 内容
     * @return
     */
    @Async("send")
    public void sendPropagateSms(String sid, String tels, String tplVars, MegamarketConf conf) {
        log.info("手机号码：[{}]，短信内容：[{}]", tels, tplVars);
        String today = DateUtil.format(new Date(),"yyyyMMdd");
        // 短信发送进度
        String key_incr = sid + "_" + today + "_incr";
        // 成功数
        String key_success = sid + "_" + today + "_success";
        // 失败数
        String key_fail = sid + "_" + today + "_fail";
        // 组装请求头
        Map<String, String> header = Maps.newHashMap();
        header.put("version", StrategyConstants.MEGAMARKET_VERSION);
        header.put("transactionId", sid);
        header.put("timestamp", DateUtil.format(new Date(), "yyyyMMddHHmmss"));
        header.put("accessCode", MEGAMARKET_ACCESSCODE);
        // 询问秘钥
        header.put("signature", getSignature(header));
        // 组装请求参数
        JSONObject body = new JSONObject();
        body.put("sendChannel", conf.getSendChannel());
        body.put("sendType", conf.getSendType());
        body.put("destPort", conf.getDestPort());
        body.put("destSubPort", conf.getDestSubPort());
        body.put("receiverNo", tels);
        /**
         * msgType -发送的内容类型: 0 -直接发送内容; 1 -模板ID;
         * msgContent -发送内容: msgType为0时，发送内容为具体短信文案; msgType为1时，发送内容为模板ID。
         * 目前短信（音乐短信模板除外）均为直发，彩信均为模板。
         */
        if ("sms".equals(conf.getSendType())){
            body.put("msgType", StrategyConstants.MEGAMARKET_MSGTYPE);
            body.put("msgContent", tplVars);
        }

        String url = MEGAMARKET_IP + MEGAMARKET_SENDMARKETINGSMS;
        JSONObject sendParams = new JSONObject();
        sendParams.put("body", body);
        sendParams.put("header", header);
        log.info(">>>>>>>>>>>>>>>请求报文:{}", sendParams.toJSONString());
        try {
            String responseString = HttpUtils.doPost(url, sendParams.toJSONString(), null);
            JSONObject response = JSONObject.parseObject(responseString);
            log.info("发送短信结果返回：手机号码:[{}], message[{}]", tels, response.toJSONString());
            redis_incr(key_incr);
            if (response.getString("resultCode").equals(MEGAMARKET_RETURN_CODE_SUCCESS)) {
                redis_incr(key_success);
            } else {
                redis_incr(key_fail);
            }
        } catch (Exception e) {
            log.error("短信发送失败，手机号码:[{}], error[{}]", tels, e.getMessage());
        }
    }

    /**
     * 拼接签名
     * 
     * @param header
     * @return
     */
    private String getSignature(Map<String, String> header) {
        StringBuilder signature = new StringBuilder();
        signature.append("version").append("=").append(header.get("version")).append("&");
        signature.append("transactionId").append("=").append(header.get("transactionId")).append("&");
        signature.append("timestamp").append("=").append(header.get("timestamp")).append("&");
        signature.append("accessCode").append("=").append(header.get("accessCode"));
        signature.append(MEGAMARKET_SALTVALUE);
        return getMD5String(signature.toString()).toLowerCase();
    }

    // 拼接短信内容
    private String getMsgContent(String tplVars) throws UnsupportedEncodingException {
        JSONObject jsonObject = new JSONObject();
        final Base64.Encoder encoder = Base64.getEncoder();
        final byte[] textByte = tplVars.getBytes("UTF-8");
        final String encodedText = encoder.encodeToString(textByte);
        jsonObject.put("content", encodedText);
        jsonObject.put("contentType", StrategyConstants.MEGAMARKET_CONTENTTYPE_TEXT);
        return jsonObject.toJSONString();
    }

}
